/*
	This file is part of Floculate.
	Copyright (C) 2008  Bill Whitacre

	Floculate is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	Floculate is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "primitives.h"

#define _A 0.525731112119133606f
#define _B 0.850650808352039932f

static unsigned int octa_indices[8][3]=
{
	{0,1,2},{0,2,3},
	{0,3,4},{0,4,1},
	{5,2,1},{5,3,2},
	{5,4,3},{5,1,4}
};

static float octa_verts[6][3]=
{    
	{0,0,-1},{1,0,0},
	{0,-1,0},{-1,0,0},
	{0,1,0},{0,0,1}
};

static unsigned int icosa_indices[20][3]=
{
	{0,4,1},{0,9,4},{9,5,4},{4,5,8},{4,8,1},    
	{8,10,1},{8,3,10},{5,3,8},{5,2,3},{2,7,3},    
	{7,10,3},{7,6,10},{7,11,6},{11,0,6},{0,1,6}, 
	{6,1,10},{9,0,11},{9,11,2},{9,2,5},{7,2,11}
};

static float icosa_verts[12][3]=
{    
	{_A,0.0,-_B},{-_A,0.0,-_B},{_A,0.0,_B},{-_A,0.0,_B},    
	{0.0,-_B,-_A},{0.0,-_B,_A},{0.0,_B,-_A},{0.0,_B,_A},    
	{-_B,-_A,0.0},{_B,-_A,0.0},{-_B,_A,0.0},{_B,_A,0.0} 
};

inline static void normalize_vert(float *a)
{
	float d=sqrtf(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]);
	a[0]/=d; a[1]/=d; a[2]/=d;
}

static void draw_recursive_tri(float *a,float *b,float *c,unsigned int div,float r)
{
	if (div==0)
	{
		glNormal3fv(a);
		glVertex3f(a[0]*r,a[1]*r,a[2]*r);
		
		glNormal3fv(b);
		glVertex3f(b[0]*r,b[1]*r,b[2]*r);
		
		glNormal3fv(c);
		glVertex3f(c[0]*r,c[1]*r,c[2]*r);
	}
	else
	{
		register unsigned int i;
		float ab[3],ac[3],bc[3];
		for (i=0; i<3; i++)
		{
			ab[i]=(a[i]+b[i])/2.0f;
			ac[i]=(a[i]+c[i])/2.0f;
			bc[i]=(b[i]+c[i])/2.0f;
		}

		normalize_vert(ab);
		normalize_vert(ac);
		normalize_vert(bc);

		draw_recursive_tri(a,ab,ac,div-1,r);
		draw_recursive_tri(b,bc,ab,div-1,r);
		draw_recursive_tri(c,ac,bc,div-1,r);
		draw_recursive_tri(ab,bc,ac,div-1,r);
    }
}

void rat_draw_sphere(unsigned int detail,float radius,int solid_base)
{
	register unsigned int i;
	switch (solid_base)
	{
	case rat_octahedron_solid_base:
		glBegin(GL_TRIANGLES);
		for (i=0; i<8; i++)
			draw_recursive_tri
			(
				octa_verts[octa_indices[i][0]],
				octa_verts[octa_indices[i][1]],
				octa_verts[octa_indices[i][2]],
				detail,radius
			);
		glEnd();
		break;
	case rat_icosahedron_solid_base:
		glBegin(GL_TRIANGLES);
		for (i=0; i<20; i++)
			draw_recursive_tri
			(
				icosa_verts[icosa_indices[i][0]],
				icosa_verts[icosa_indices[i][1]],
				icosa_verts[icosa_indices[i][2]],
				detail,radius
			);
		glEnd();
		break;
	default:
		break;
	};
}

void rat_draw_cylinder(unsigned int radsegs,unsigned int linsegs,unsigned int rings,float radius,float length)
{
	register int k,j,i=0;
	float *circleverts=(float *)malloc(sizeof(float)*radsegs*2);
	float sqt,theta,ringsegs,step=(2.0f*MATH_PI)/(float)radsegs;
	
	radsegs=radsegs<4?4:radsegs;
	linsegs=linsegs<1?1:linsegs;
	rings=rings<1?1:rings;
	
	// generate the circle vertices
	for (theta=0.0f; theta<=MATH_PI*2.0f&&i<radsegs; theta+=step)
	{
		circleverts[i*2]=cosf(-theta);
		circleverts[i*2+1]=sinf(-theta);
		++i;
	}
	
	// draw the caps
	step=radius/(float)rings;
	glBegin(GL_TRIANGLE_FAN);
		glNormal3f(0,1,0);
		glVertex3f(0,length/2.0f,0);
		for (i=0; i<radsegs+1; i++)
		{
			j=i%radsegs;
			
			glNormal3f(0,1,0);
			glVertex3f(circleverts[j*2]*step,length/2.0f,circleverts[j*2+1]*step);
		}
	glEnd();
	for (i=0; i<rings-1; i++)
	{
		glBegin(GL_QUAD_STRIP);
			for (k=0; k<radsegs+1; k++)
			{
				j=k%radsegs;
			
				glNormal3f(0,1,0);
				glVertex3f(circleverts[j*2]*step*(i+1),length/2.0f,circleverts[j*2+1]*step*(i+1));
			
				glNormal3f(0,1,0);
				glVertex3f(circleverts[j*2]*step*(i+2),length/2.0f,circleverts[j*2+1]*step*(i+2));
			}
		glEnd();
	}
	glBegin(GL_TRIANGLE_FAN);
		glNormal3f(0,-1,0);
		glVertex3f(0,-length/2.0f,0);
		for (i=radsegs; i>=0; i--)
		{
			j=i%radsegs;
			
			glNormal3f(0,-1,0);
			glVertex3f(circleverts[j*2]*step,-length/2.0f,circleverts[j*2+1]*step);
		}
	glEnd();
	for (i=0; i<rings-1; i++)
	{
		glBegin(GL_QUAD_STRIP);
			for (k=radsegs; k>=0; k--)
			{
				j=k%radsegs;
			
				glNormal3f(0,-1,0);
				glVertex3f(circleverts[j*2]*step*(i+1),-length/2.0f,circleverts[j*2+1]*step*(i+1));
			
				glNormal3f(0,-1,0);
				glVertex3f(circleverts[j*2]*step*(i+2),-length/2.0f,circleverts[j*2+1]*step*(i+2));
			}
		glEnd();
	}
	
	// draw the cylinder
	step=length/(float)linsegs;
	for (i=0; i<linsegs; i++)
	{
		float segya=length/2.0f-step*(float)i;
		float segyb=length/2.0f-step*((float)i+1);
		glBegin(GL_QUAD_STRIP);
			for (k=0; k<radsegs+1; k++)
			{
				j=k%radsegs;
			
				glNormal3f(circleverts[j*2],0,circleverts[j*2+1]);
				glVertex3f(circleverts[j*2]*radius,segya,circleverts[j*2+1]*radius);
			
				glNormal3f(circleverts[j*2],0,circleverts[j*2+1]);
				glVertex3f(circleverts[j*2]*radius,segyb,circleverts[j*2+1]*radius);
			}
		glEnd();
	}
	
	free((void *)circleverts);
}

